package 反射;

/*
    类的加载过程
        当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化

            第一步:类的加载(Load):将类的class文件读入内存,并为之创建一个java.lang.Class对象,此过程由类加载器完成

            第二步:类的链接(Link):将类的二进制数据合并到JRE中

            第三步:类的初始化(Initialize):JVM负责对类进行初始化

        解析:
             加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口(即引用地址),所有需要访问和使用类数据只能通过这个Class对象,这个加载的过程需要类加载器参与
             链接:将Java类的二进制代码合并到JVM的运行状态之中的过程。
                 验证:确保加载的类信息符合JVM规范,例如:以cafe开头,没有安全方面的问题
                 准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配
                 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程
             初始化:
                 执行类构造器<clinit>()方法的过程,类构造器<clinit>()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的,(类构造器是构造类信息的,不是构造该类对象的构造器)
                 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化
                 虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步  --> 保证只执行一次

        发生类初始化的时机
            1、类的主动引用(一定会发生类的初始化)
                 当虚拟机启动,先初始化main方法所在的类
                 new一个类的对象
                 调用类的静态成员(除了final常量)和静态方法
                 使用java.lang.reflect包的方法对类进行反射调用
                 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类
            2、类的被动引用(不会发生类的初始化)
                 当访问一个静态域时,只有真正声明这个域的类才会被初始化(当通过子类引用父类的静态变量,不会导致子类初始化)
                 通过数组定义类引用,不会触发此类的初始化
                 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)
 */
public class ClassLoadingTest03 {
    public static void main(String[] args) {
        System.out.println(A.m);
    }
}

/*
    第二步:链接结束后m=0
    第三步:初始化后,m的值由<clinit>()方法执行决定,这个A的类构造器<clinit>()方法由类变量的赋值和静态代码块中的语句按照顺序合并产生,类似于
     <clinit>(){
       m = 300;
       m = 100;
     }
 */
class A {
    static {
        m = 300;
    }

    static int m = 100;
}
